#ifndef __SERVICE_M__
#define __SERVICE_M__
#include "data.hpp"
#include "httplib.h"

extern Cloud::DataManager *_data;

namespace Cloud
{
    class Service
    {
    public:
        Service()
        {
            Config *cfg = Config::GetInstance();
            _server_ip = cfg->GetServerIp();
            _server_port = cfg->GetServerPort();
            _download_prefix = cfg->GetDownLoadPrefix();
        }
        bool RunModule()
        {
            _server.Post("/upload", Upload);
            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow);
            std::string download_url = _download_prefix + "(.*)";//正则表达式，匹配任意多个字符(用于获取下载的文件名)
            _server.Get(download_url, Download);
            _server.listen(_server_ip.c_str(), _server_port);
            return true;
        }
    private:
        static void Upload(const httplib::Request &req, httplib::Response &rsp)
        {
            auto ret = req.has_file("file");//查看是否有文件上传
            if(ret == false)
            {
                rsp.status = 400;
                return;
            }
            //有文件->获取文件内容
            const auto &file = req.get_file_value("file");//filename文件名称  econtent文件内容
            std::string back_dir = Config::GetInstance()->GetBackDir();
            std::string realpath = back_dir + FileUtil(file.filename).FileName();
            //将数据写入文件中
            FileUtil tmp(realpath);
            tmp.SetContent(file.content);
            //更新备份数据信息
            BackUpInfo info;
            info.NewBackUpInfo(realpath);
            _data->UpData(info);
        }
        static std::string TimeToDate(time_t time)
        {   
            std::string ret = std::ctime(&time);
            return ret;
        }
        static void ListShow(const httplib::Request &req, httplib::Response &rsp)
        {
            //获取所有的备份信息，从备份文件中读取
            std::vector<BackUpInfo> array;
            _data->GetAll(&array);
            //组织HTML页面格式
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for(auto &a : array)
            {
                std::string filename = FileUtil(a.real_path).FileName();
                ss << "<tr>";
                ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>";
                ss << R"(<td align="right">)" << TimeToDate(a.mtime) << "</td>";
                ss << R"(<td align="right">)" << a.fsize / 1024 << "k</td>";
                ss << "<tr>";
            }
            ss << "</table></body></html>";
            //构建响应
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html;charset=UTF-8");
            rsp.status = 200;
        }
        static std::string GetETag(const BackUpInfo &info) 
        {
				// etg :  filename-fsize-mtime
				FileUtil fu(info.real_path);
				std::string etag = fu.FileName();
				etag += "-";
				etag += std::to_string(info.fsize);
				etag += "-";
				etag += std::to_string(info.mtime);
				return etag;
		}
        static void Download(const httplib::Request &req,httplib::Response &rsp)
        {
            //1. 获取客户端请求的资源路径path   req.path
			//2. 根据资源路径，获取文件备份信息
            BackUpInfo info;
            _data->GetOneByUrl(req.path, &info);
            //3. 判断文件是否被压缩，如果被压缩，要先解压缩, 
            if(info.pack_flag == true)
            {
                FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path);//将文件解压到备份目录下
                //3.1 删除压缩包，修改备份信息（已经没有被压缩）
                fu.Remove();
                info.pack_flag = false;
                _data->UpData(info);
            }

            //断电续传思想：
            //服务器在响应给客户端的时候带有一个Accept-Ranges字段,该字段表示了是否支持断电续传,同时还有一个ETag字段
            //该字段是作为文件的唯一标识符，如果客户端在下载的时候异常中断了，那么再次下载的时候就需要根据这个字段
            //判断要下载的文件在异常的这段时间内是否有被修改，如果修改了的话，那么服务端这边得Etag字段就会发生改变
            //通过比对两端的Etag字段就可以判断是可以继续下载还是要重新下载

            //客户端的Range就是请求的下载区间 100-21312 / 100-这个表示100到文件末尾
            //客户端If-Range: "文件唯一标识"

            //4.0:判断请求中是否有If-Range字段(断电续传的判断)
            bool retrans = false;
            std::string old_etag;
            if (req.has_header("If-Range")) {
                old_etag = req.get_header_value("If-Range");
                if (old_etag == GetETag(info)) {
                    retrans = true;//有If-Range字段且，这个字段的值与请求文件的最新etag一致则符合断点续传
                }
            }
            //4. 读取文件数据，放入rsp.body中
            FileUtil file(info.real_path);
            if (retrans == false){
                file.GetContent(&rsp.body);
                //5. 设置响应头部字段： ETag， Accept-Ranges: bytes
                rsp.set_header("Accept-Ranges", "bytes");
                rsp.set_header("ETag", GetETag(info));
                rsp.set_header("Content-Type", "application/octet-stream");
                rsp.status = 200;
            }else {
                //httplib内部实现了对于区间请求也就是断点续传请求的处理
                //只需要我们用户将文件所有数据读取到rsp.body中，它内部会自动根据请求
                //区间，从body中取出指定区间数据进行响应
                file.GetContent(&rsp.body);
                rsp.set_header("Accept-Ranges", "bytes");
                rsp.set_header("ETag", GetETag(info));
                rsp.set_header("Content-Type", "application/octet-stream");
                rsp.status = 206;//区间请求响应的是206*****
            }
        }
    private:
        std::string _server_ip;
        int _server_port;
        std::string _download_prefix;
        httplib::Server _server;
    };
}

#endif